
#include "nane/RenderSystem/RenderSystem.h"
#include "nane/RenderSystem/RenderSystemImpl_.h"
#include "nane/RenderSystem/VertexBufferImpl_.h"
#include "nane/RenderSystem/IndexBufferImpl_.h"
#include "nane/RenderSystem/TextureImpl_.h"
#include "nane/RenderSystem/VertexShaderImpl_.h"
#include "nane/RenderSystem/PixelShaderImpl_.h"
#include "nane/RenderSystem/ConstantBufferImpl_.h"
#include "nane/core/assert.h"
#include "nane/core/log.h"

namespace nane
{
    ////////////////////////////////////////////////////////////////////////////////////////////////
    RenderSystem::RenderSystem()
        : SubSystem<IRenderSystem>( new Implementation() )
        , m( static_cast<Implementation*>(SubSystem<IRenderSystem>::m) )
    {
        
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    RenderSystem::~RenderSystem()
    {
        Shutdown();
        delete m;
        m = NULL;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void RenderSystem::Shutdown()
    {
        if( m->interface != NULL )
        {
            m->interface->SetVertexShader(NULL);
            m->currentVertexShader = NullPtr;
            m->interface->SetPixelShader(NULL);
            m->currentPixelShader = NullPtr;
        }
        SubSystem<IRenderSystem>::Shutdown();
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool RenderSystem::InitializeContext( IWindow::OSHandle _windowHandle, uint32 _resWidth, uint32 _resHeight )
    {
        NANE_ASSERT_RV( m->interface != NULL, "RenderSystem must be initialized", false );
        return m->interface->InitializeContext(_windowHandle, _resWidth, _resHeight);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    VertexBuffer RenderSystem::CreateVertexBuffer( EResourceUsage _usage, uint32 _verticesNum, uint32 _vertexSize
                                                   , VertexElement* _elements, uint32 _elementsNum, byte* _initData )
    {
        NANE_ASSERT_RV( m->interface != NULL, "RenderSystem must be initialized", NullPtr );
        VertexBuffer::Implementation* vertexBufferImpl = NULL;
        IVertexBuffer* vertexBufferInterface = m->interface->CreateVertexBuffer(_usage.raw_value(), _verticesNum, _vertexSize, _elements, _elementsNum, _initData);
        if( vertexBufferInterface != NULL )
        {
            vertexBufferImpl = new VertexBuffer::Implementation(m, vertexBufferInterface, _vertexSize * _verticesNum);
        }
        return VertexBuffer(vertexBufferImpl);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void RenderSystem::SetVertexBuffer( uint16 _streamNumber, VertexBuffer& _vertexBuffer )
    {
        NANE_ASSERT_R( m->interface != NULL, "RenderSystem must be initialized" );
        IVertexBuffer* vertexBufferInterface = _vertexBuffer != NullPtr ? _vertexBuffer.m->interface : NULL;
        m->interface->SetVertexBuffer(_streamNumber, vertexBufferInterface);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    IndexBuffer RenderSystem::CreateIndexBuffer( EResourceUsage _usage, uint32 _indiciesNum, uint16* _initData )
    {
        NANE_ASSERT_RV( m->interface != NULL, "RenderSystem must be initialized", NullPtr );
        IndexBuffer::Implementation* indexBufferImpl = NULL;
        IIndexBuffer* indexBufferInterface = m->interface->CreateIndexBuffer(_usage.raw_value(), _indiciesNum, _initData);
        if( indexBufferInterface != NULL )
        {
            indexBufferImpl = new IndexBuffer::Implementation(m, indexBufferInterface, _indiciesNum * sizeof(uint16));
        }
        return IndexBuffer(indexBufferImpl);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void RenderSystem::SetIndexBuffer( IndexBuffer& _indexBuffer )
    {
        NANE_ASSERT_R( m->interface != NULL, "RenderSystem must be initialized" );
        IIndexBuffer* indexBufferInterface = _indexBuffer != NullPtr ? _indexBuffer.m->interface : NULL;
        m->interface->SetIndexBuffer(indexBufferInterface);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    Texture RenderSystem::CreateTexture( EResourceUsage _usage, uint32 _width, uint32 _height, EPixelFormat _format )
    {
        NANE_ASSERT_RV( m->interface != NULL, "RenderSystem must be initialized", NullPtr );
        Texture::Implementation* textureImpl = NULL;
        ITexture* iTexture = m->interface->CreateTexture(_usage.raw_value(), _width, _height, _format.raw_value());
        if( iTexture != NULL )
        {
            textureImpl = new Texture::Implementation(m, iTexture, _width, _height);
        }
        return Texture(textureImpl);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void RenderSystem::SetTexture( uint32 _stage, Texture& _texture )
    {
        NANE_ASSERT_R( m->interface != NULL, "RenderSystem must be initialized" );
        ITexture* iTexture = _texture != NullPtr ? _texture.m->interface : NULL;
        m->interface->SetTexture(_stage, iTexture);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool RenderSystem::BeginScene()
    {
        NANE_ASSERT_RV( m->interface != NULL, "RenderSystem must be initialized", false );
        m->interface->BeginScene();
        return true;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void RenderSystem::EndScene()
    {
        NANE_ASSERT_R( m->interface != NULL, "RenderSystem must be initialized" );
        m->interface->EndScene();
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void RenderSystem::DrawPrimitive( EPrimitiveType _type, uint32 _startVertex, uint32 _primitiveCount )
    {
        NANE_ASSERT_R( m->interface != NULL, "RenderSystem must be initialized" );
        m->interface->DrawPrimitive(_type.raw_value(), _startVertex, _primitiveCount);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void RenderSystem::DrawIndexedPrimitive( EPrimitiveType _type, uint32 _baseVertexIndex, uint32 _minIndex
                             , uint32 _verticesNum, uint32 _startIndex, uint32 _primitiveCount )
    {
        NANE_ASSERT_R( m->interface != NULL, "RenderSystem must be initialized" );
        m->interface->DrawIndexedPrimitive(_type.raw_value(), _baseVertexIndex, _minIndex, _verticesNum, _startIndex, _primitiveCount);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void RenderSystem::ClearRenderTarget( EFrameBufferType _type, uint32 _color, float _depth, uint16 _stencil )
    {
        NANE_ASSERT_R( m->interface != NULL, "RenderSystem must be initialized" );
        m->interface->ClearRenderTarget(_type.raw_value(), _color, _depth, _stencil);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void RenderSystem::SwapBuffers()
    {
        NANE_ASSERT_R( m->interface != NULL, "RenderSystem must be initialized" );
        m->interface->SwapBuffers();
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void RenderSystem::SetProjectionMatrix( const float* _projection )
    {
        NANE_ASSERT_R( m->interface != NULL, "RenderSystem must be initialized" );
        m->interface->SetProjectionMatrix(_projection);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void RenderSystem::SetModelViewMatrix( const float* _modelview )
    {
        NANE_ASSERT_R( m->interface != NULL, "RenderSystem must be initialized" );
        m->interface->SetModelViewMatrix(_modelview);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void RenderSystem::SetLightingEnable( bool _enable )
    {
        NANE_ASSERT_R( m->interface != NULL, "RenderSystem must be initialized" );
        m->interface->SetLightingEnable(_enable);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void RenderSystem::SetCullMode( ECullMode _mode )
    {
        NANE_ASSERT_R( m->interface != NULL, "RenderSystem must be initialized" );
        m->interface->SetCullMode(_mode.raw_value());
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    ConstantBuffer RenderSystem::CreateConstantBuffer( uint32 _slots )
    {
        NANE_ASSERT_RV( m->interface != NULL, "RenderSystem must be initialized", NullPtr );
        NANE_ASSERT_RV( _slots != 0, "size must be > 0", NullPtr );
        
        ConstantBuffer::Implementation* constantBufferImpl = NULL;
        IConstantBuffer* constantBufferInterface = m->interface->CreateConstantBuffer(_slots * sizeof(float) * 4);
        if( constantBufferInterface != NULL )
        {
            constantBufferImpl = new ConstantBuffer::Implementation(m, constantBufferInterface, _slots);
        }
        return ConstantBuffer(constantBufferImpl);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    VertexShader RenderSystem::CreateVertexShader( const InputSeekablePtr& _input )
    {
        NANE_ASSERT_RV( m->interface != NULL, "RenderSystem must be initialized", NullPtr );
        NANE_ASSERT_RV( _input != NullPtr, "Input must be valid", NullPtr )
        
        byte* buffer = new byte[_input->Size()];
        _input->Read(buffer, _input->Size());
        
        VertexShader::Implementation* vertexShaderImpl = NULL;
        IVertexShader* vertexShaderInterface = m->interface->CreateVertexShader(buffer, _input->Size());;
        delete[] buffer;

        if( vertexShaderInterface != NULL )
        {
            vertexShaderImpl = new VertexShader::Implementation(m, vertexShaderInterface);
        }

        return VertexShader(vertexShaderImpl);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void RenderSystem::SetVertexShader( VertexShader& _vertexShader )
    {
        NANE_ASSERT_R( m->interface != NULL, "RenderSystem must be initialized" );
        
        if( _vertexShader != m->currentVertexShader )
        {
            IVertexShader* vertexShaderInterface = _vertexShader != NullPtr ? _vertexShader.m->interface : NULL;
            if( m->interface->SetVertexShader(vertexShaderInterface) == false )
            {
                LOG_WARNING("Failed to set vertex shader");
            }
            m->currentVertexShader = _vertexShader;
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void RenderSystem::SetVertexShaderConstantBuffer( uint32 _slot, ConstantBuffer& _constantBuffer )
    {
        NANE_ASSERT_R( m->interface != NULL, "RenderSystem must be initialized" );
        
        IConstantBuffer* constantBufferInterface = _constantBuffer != NullPtr ? _constantBuffer.m->interface : NULL;
        if( m->interface->SetVertexShaderConstantBuffer(_slot, constantBufferInterface) == false )
        {
            LOG_WARNING("Failed to set vertex shader constant buffer to slot %1") << _slot;
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    PixelShader RenderSystem::CreatePixelShader( const InputSeekablePtr& _input )
    {
        NANE_ASSERT_RV( m->interface != NULL, "RenderSystem must be initialized", NullPtr );
        NANE_ASSERT_RV( _input != NullPtr, "Input must be valid", NullPtr );

        byte* buffer = new byte[_input->Size()];
        _input->Read(buffer, _input->Size());
        
        PixelShader::Implementation* pixelShaderImpl = NULL;
        IPixelShader* pixelShaderInterface = m->interface->CreatePixelShader(buffer, _input->Size());
        delete[] buffer;
        
        if( pixelShaderInterface != NULL )
        {
            pixelShaderImpl = new PixelShader::Implementation(m, pixelShaderInterface);
        }
        
        return PixelShader(pixelShaderImpl);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void RenderSystem::SetPixelShader( PixelShader& _pixelShader )
    {
        NANE_ASSERT_R( m->interface != NULL, "RenderSystem must be initialized" );
        
        if( _pixelShader != m->currentPixelShader )
        {
            IPixelShader* pixelShaderInterface = _pixelShader != NullPtr ? _pixelShader.m->interface : NULL;
            if( m->interface->SetPixelShader(pixelShaderInterface) == false )
            {
                LOG_WARNING("Failed to set pixel shader");
            }
            m->currentPixelShader = _pixelShader;
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void RenderSystem::SetPixelShaderConstantBuffer( uint32 _slot, ConstantBuffer& _constantBuffer )
    {
        NANE_ASSERT_R( m->interface != NULL, "RenderSystem must be initialized" );
        
        IConstantBuffer* constantBufferInterface = _constantBuffer != NullPtr ? _constantBuffer.m->interface : NULL;
        if( m->interface->SetPixelShaderConstantBuffer(_slot, constantBufferInterface) == false )
        {
            LOG_WARNING("Failed to set pixel shader constant buffer to slot %1") << _slot;
        }        
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
}   // namespace nane
